/*
 * @Description: 
 * @Version: 1.0
 * @Autor: ziwei
 * @Date: 2021-09-26 14:22:55
 * @LastEditors: ziwei
 * @LastEditTime: 2021-09-27 12:32:37
 */
import { reactive, watch } from "vue";
import store from "../store";
import { storeKey } from "./injectKey";
import ModuleCollection from './module/module-collection';
import { forEachValue, isPromise } from "./utils";

//根据路径 获取store上面最新的状态
function getNestedState(state,path) {
  return path.reduce((state,key)=>state[key],state)
}

//后续我们会将store.state 用reactive包裹
function installModule(store,rootState,path,module) {//递归安装
  let isRoot = !path.length;//如果数组是空数组说明是根 否则不是
  const namespaced = store._modules.getNestedState(path);

  if(!isRoot){//[aCount Conut]
    let parentState = path.slice(0,-1).reduce((state,key)=>state[key],rootState)
    store._withCommit(()=>{
      parentState[path[path.length-1]] = module.state
    })
  }

  module.forEachGetter((getter,key)=>{
    store._wrappedGetters[namespaced+key] = ()=> {
      return getter(getNestedState(store.state,path));//如果直接使用模块上自己的状态 此状态不是响应式的
    }
  })

  //mutation
  module.forEachMutation((mutation,key)=>{
    const entry = store._moutations[namespaced+key] || (store._moutations[key]=[])
    entry.push((payload)=>{
      mutation.call(store,getNestedState(store.state,path),payload);
    })
  })

  //action mutation 和action的区别 action 执行后会返回promise
  module.forEachAction((action,key)=>{
    const entry = store._actions[namespaced+key] || (store._actions[key]=[])
    entry.push((payload)=>{
      let res = action.call(store,store,payload);
      //res是不是promise 不是的话包装一下
      if(!isPromise(res)){
        return Promise.resolve(res);
      }
      return res;
    })
  })
  
  //不是根的模块要安装到根上 子模块再次安装
  module.forEachChild((child,key)=>{//aCount bCount
    installModule(store,rootState,path.concat(key),child);
  })
}

function resetStoreState(store,state) {
  //状态
  store._state = reactive({data:state});
  const wrappedGetters = store._wrappedGetters;
  store.getters = {}
  //计算属性
  forEachValue(wrappedGetters,(getter,key)=>{
    Object.defineProperty(store.getters,key,{
      get:getter,
      enumerator:true
    })
  })
  if(store.strict){
    enableStrictMode(store);
  }
}

function enableStrictMode(store) {
  watch(()=>store._state.data,()=>{//监控数据变化 数据变化后执行回调函数 effect
    console.assert(store._commiting,'do not mutate vuex state outside mutation handlers');
  },{deep:true,flush:'sync'});//默认watchApi是异步的，这里改成同步的监控
}
export default class Store {
  _withCommit(fn){//切片函数
    const commiting = this._commiting;
    this._commiting = true;
    fn();
    this._commiting=commiting;
  }
  
  constructor(options) {
    const store = this;
    store._modules = new ModuleCollection(options);
    
    store._moutations = Object.create(null)
    store._actions = Object.create(null)
    store._wrappedGetters = Object.create(null)

    this.strict = options.strict||false;//是不是严格模式

    //调用的时候 知道是mutation,mutation里面得写同步代码
    this._commiting = false;
    //在mutiation之前添加一个状态 _commiting=true 调用mutaion 会更改状态 我就监控这个状态，如果当前这个状态变化的时候

    //定义状态 模块的安装
    let state = store._modules.root.state;//根状态
    installModule(store,state,[],store._modules.root);
    resetStoreState(store,state);//把方法定义在store上
    console.log(store);

    //把状态定义到 store.state.aCount.aCount.count
   //{state action mutations getter modules}
   store._subscribes = [];
   options.plugins.forEach(plugin=>plugin(store));
  }
  subscribe(fn){
    //严格模式下 不能直接修改状态
    this._subscribes.push(fn);
  }
  get state(){
    return this._state.data
  }

  replaceState(newState){
    this._withCommit(()=>{
      this._state.data = newState
    })
  }

  commit = (type,payload)=>{
    const entry = this._moutations[type]||[];
    this._withCommit(()=>{
      entry && entry.forEach(handler=>handler(payload))
    })
    this._subscribes.forEach(sub=>sub({type,payload},this.state))
  }

  dispatch = (type,payload)=>{
    const entry = this._actions[type]||[];
    return Promise.all(entry.map(handler=>handler(payload)))
  }

  install(app,injectKey){//app就是对象 createApp().use(store,my)
    //全局暴露一个变量 暴露的是store的实例
    app.provide(injectKey||storeKey,this);//会根app增加一个_provides,子组件会向上查找
    //Vue.prototype.$store = this;
    app.config.globalProperties.$store = this;//增添$store属性
  }
  
  registerModule(path,rawModule){
    const store = this;
    if(typeof path == 'string') path=[path]
    //要在原来的基础上新增一个
    const newModule = store._modules.register(rawModule,path)
    //把原来模块安装上 

    installModule(store,store.state,path,newModule)
    //重置容器
    resetStoreState(store,store.state);
  }
}

//格式化用户的参数 实现根据自己的需要 后续使用时方便
// root = {
//   _raw:rootModule,
//   state:rootModule.state,
//   _children:{
//     aCount:{
//       _raw:aModule,
//       state:aModule.state,
//       _children:{}
//     }
//   },
//   bcount:{
//     _raw:bModlue,
//     state:bModlue.state,
//     _children:{}
//   }
// }

//先执行插件 订阅把函数都保存起来 状态变化调用订阅函数 用户刷新调用repalceState 替换为新的状态